package com.z.system.exception;

import java.util.HashMap;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.stream.Collectors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.NoHandlerFoundException;

import com.z.system.utils.R;

/**
 * 全局异常处理器
 * 使用@ControllerAdvice注解捕获所有Controller中的异常
 */
@ControllerAdvice
public class GlobalExceptionHandler {
    
    /**
     * 构造函数
     */
    public GlobalExceptionHandler() {
    }
    
    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
    
    /**
     * 处理业务异常
     * @param e 业务异常
     * @return 错误响应
     */
    @ExceptionHandler(BusinessException.class)
    @ResponseBody
    public R<?> handleBusinessException(BusinessException e) {
        logger.error("业务异常: {}", e.getMessage(), e);
        return R.error(e.getCode(), e.getMessage());
    }
    
    /**
     * 处理基础异常
     * @param e 基础异常
     * @return 错误响应
     */
    @ExceptionHandler(BaseException.class)
    @ResponseBody
    public R<?> handleBaseException(BaseException e) {
        logger.error("基础异常: {}", e.getMessage(), e);
        return R.error(e.getCode(), e.getMessage());
    }
    
    /**
     * 处理运行时异常
     * @param e 运行时异常
     * @return 错误响应
     */
    @ExceptionHandler(RuntimeException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public R<?> handleRuntimeException(RuntimeException e) {
        logger.error("运行时异常: {}", e.getMessage(), e);
        
        // 针对用户自定义的RuntimeException消息进行友好处理
        if (e.getMessage() != null && !e.getMessage().contains("java.lang")) {
            return R.error(400, e.getMessage());
        }
        return R.error(500, "操作失败，请稍后重试");
    }
    
    /**
     * 处理非法参数异常
     * @param e 非法参数异常
     * @return 错误响应
     */
    @ExceptionHandler(IllegalArgumentException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public R<?> handleIllegalArgumentException(IllegalArgumentException e) {
        logger.error("非法参数异常: {}", e.getMessage(), e);
        return R.error(400, "参数格式不正确: " + e.getMessage());
    }
    
    /**
     * 处理数据完整性违反异常
     * @param e 数据完整性违反异常
     * @return 错误响应
     */
    @ExceptionHandler(DataIntegrityViolationException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public R<?> handleDataIntegrityViolationException(DataIntegrityViolationException e) {
        logger.error("数据完整性异常: {}", e.getMessage(), e);
        
        // 解析异常信息，提供更友好的错误消息
        String message = "数据操作失败";
        if (e.getMessage() != null) {
            if (e.getMessage().contains("unique constraint")) {
                message = "数据已存在，无法重复添加";
            } else if (e.getMessage().contains("foreign key constraint")) {
                message = "该数据已被关联使用，无法删除";
            }
        }
        
        return R.error(400, message);
    }
    
    /**
     * 处理认证异常
     * @param e 认证异常
     * @return 401未授权响应
     */
    @ExceptionHandler(AuthenticationException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    public R<?> handleAuthenticationException(AuthenticationException e) {
        logger.error("认证异常: {}", e.getMessage());
        return R.error(401, "认证失败: " + e.getMessage());
    }
    
    /**
     * 处理权限拒绝异常
     * @param e 权限拒绝异常
     * @return 403禁止访问响应
     */
    @ExceptionHandler(AccessDeniedException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.FORBIDDEN)
    public R<?> handleAccessDeniedException(AccessDeniedException e) {
        logger.error("权限拒绝异常: {}", e.getMessage());
        return R.error(403, "没有权限访问该资源");
    }
    
    /**
     * 处理参数验证异常
     * @param e 参数验证异常
     * @return 400错误请求响应
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public R<?> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        logger.error("参数验证异常: {}", e.getMessage());
        
        // 收集所有字段验证错误信息
        Map<String, String> errors = new HashMap<>();
        e.getBindingResult().getAllErrors().forEach((error) -> {
            String fieldName = ((FieldError) error).getField();
            String errorMessage = error.getDefaultMessage();
            errors.put(fieldName, errorMessage);
        });
        
        return R.error(400, "参数验证失败", errors);
    }
    
    /**
     * 处理缺少请求参数异常
     * @param e 缺少请求参数异常
     * @return 400错误请求响应
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public R<?> handleMissingServletRequestParameterException(MissingServletRequestParameterException e) {
        logger.error("缺少请求参数异常: {}", e.getMessage());
        return R.error(400, "缺少必要的请求参数: " + e.getParameterName());
    }
    
    /**
     * 处理参数类型不匹配异常
     * @param e 参数类型不匹配异常
     * @return 400错误请求响应
     */
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public R<?> handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e) {
        logger.error("参数类型不匹配异常: {}", e.getMessage());
        return R.error(400, "参数类型不匹配: " + e.getName() + " 应该是 " + e.getRequiredType().getSimpleName() + " 类型");
    }
    
    /**
     * 处理方法不支持异常
     * @param e 方法不支持异常
     * @return 405方法不允许响应
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
    public R<?> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
        logger.error("方法不支持异常: {}", e.getMessage());
        return R.error(405, "不支持的请求方法: " + e.getMethod());
    }
    
    /**
     * 处理资源未找到异常
     * @param e 资源未找到异常
     * @return 404未找到响应
     */
    @ExceptionHandler(NoHandlerFoundException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public R<?> handleNoHandlerFoundException(NoHandlerFoundException e) {
        logger.error("资源未找到异常: {}", e.getMessage());
        return R.error(404, "请求的资源不存在");
    }
    
    /**
     * 处理元素不存在异常（如JPA的findById返回空）
     * @param e 元素不存在异常
     * @return 404未找到响应
     */
    @ExceptionHandler(NoSuchElementException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public R<?> handleNoSuchElementException(NoSuchElementException e) {
        logger.error("元素不存在异常: {}", e.getMessage());
        return R.error(404, "请求的资源不存在");
    }
    
    /**
     * 处理密码错误异常
     * @param e 密码错误异常
     * @return 401未授权响应
     */
    @ExceptionHandler(BadCredentialsException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    public R<?> handleBadCredentialsException(BadCredentialsException e) {
        logger.error("密码错误异常: {}", e.getMessage());
        return R.error(401, "用户名或密码错误");
    }
    
    /**
     * 处理所有未捕获的异常
     * @param e 未捕获的异常
     * @return 500服务器错误响应
     */
    @ExceptionHandler(Exception.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public R<?> handleException(Exception e) {
        logger.error("系统异常: {}", e.getMessage(), e);
        
        // 在开发环境可以返回更详细的错误信息，生产环境返回通用错误
        // 这里简单判断，实际项目中应该根据环境变量来控制
        if ("dev".equals(System.getProperty("spring.profiles.active", "prod"))) {
            Map<String, Object> errorDetail = new HashMap<>();
            errorDetail.put("message", e.getMessage());
            errorDetail.put("stackTrace", getStackTraceAsString(e));
            return R.error(500, "服务器内部错误", errorDetail);
        }
        
        return R.error(500, "服务器内部错误，请联系管理员");
    }
    
    /**
     * 将异常堆栈转换为字符串
     * @param throwable 异常对象
     * @return 堆栈信息字符串
     */
    private String getStackTraceAsString(Throwable throwable) {
        return java.util.Arrays.stream(throwable.getStackTrace())
                .map(StackTraceElement::toString)
                .collect(Collectors.joining("\n"));
    }
}